use std::sync::Arc;

use tauri::Window;
use tokio::{
    io::{AsyncReadExt, AsyncWriteExt},
    sync::Semaphore,
};
use tokio_serial::{SerialPortBuilderExt, SerialStream};

use crate::{
    error::{new_proto_err, new_static_err, AppErr},
    utils::{get_mut, new_bytes},
};

unsafe impl Send for SerialPort {}
unsafe impl Sync for SerialPort {}

pub struct SerialPort {
    stream: SerialStream,
    exit_sem: Semaphore,
    window: Window,
}

pub type SharedPort = Arc<SerialPort>;

const HEAD0: u8 = 0xE1;
const HEAD1: u8 = 0x1E;
const END: u8 = 0xEF;

const EMIT_RECV_BYTES: &'static str = "emit-recv-bytes";

impl SerialPort {
    pub fn new(name: &str, baud_rate: u32, window: Window) -> Result<SharedPort, AppErr> {
        let stream = tokio_serial::new(name, baud_rate).open_native_async()?;
        let port = SerialPort {
            stream,
            exit_sem: Semaphore::new(0),
            window,
        };
        let port = Arc::new(port);
        tokio::spawn(serial_port_recv_loop(port.clone()));
        Ok(port)
    }

    pub async fn write(&self, buf: &[u8]) -> Result<(), AppErr> {
        let stream = get_mut(&self.stream);
        stream.write_all(buf).await?;
        Ok(())
    }

    pub fn close(&self) {
        self.exit_sem.add_permits(1);
    }

    async fn sync(&self) -> Result<usize, AppErr> {
        let stream = get_mut(&self.stream);
        let mut flag = false;
        let mut buf: [u8; 1] = [0; 1];
        loop {
            let n = stream.read(&mut buf).await?;
            if n <= 0 {
                return new_static_err("串口断开");
            }
            if flag && (buf[0] == HEAD1) {
                break;
            }
            flag = buf[0] == HEAD0;
        }
        let n = stream.read(&mut buf).await?;
        if n <= 0 {
            return new_static_err("串口端口");
        }
        Ok(buf[0] as usize)
    }

    async fn recv(&self) -> Result<Box<[u8]>, AppErr> {
        let len = self.sync().await?;
        if len < 7 {
            return new_proto_err("invalid len");
        }
        let len = len - 3;
        let mut buf = new_bytes(len);
        let stream = get_mut(&self.stream);
        stream.read_exact(&mut buf).await?;

        let mut sum = 0;
        for i in 0..(len - 2) {
            sum = sum ^ buf[i];
        }

        if sum != buf[len - 2] {
            return new_proto_err("invalid check sum");
        }
        if END != buf[len - 1] {
            return new_proto_err("invalid end");
        }

        Ok(buf)
    }
}

async fn serial_port_recv_loop(port: SharedPort) {
    loop {
        let buf = tokio::select! {
            _ = port.exit_sem.acquire() => {
                println!("port closed");
                break;
            }

            ret = port.recv() => {
                match ret {
                    Ok(buf) => buf,
                    Err(AppErr::Proto(e)) => {
                        println!("proto:{}", e);
                        continue;
                    }
                    Err(e) => {
                        println!("err:{}", e);
                        break;
                    }
                }
            }
        };
        _ = port.window.emit(EMIT_RECV_BYTES, buf);
    }
}
